package com.fakeDYChat.common.common.service;

import com.fakeDYChat.common.common.exception.CommonErrorEnum;
import com.fakeDYChat.common.common.utils.AssertUtil;
import lombok.SneakyThrows;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 分布式锁执行方法业务类
 **/
@Service
public class LockService {
    private final RedissonClient redissonClient;

    public LockService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     * 上锁并执行模板方法，返回时解锁，有返回值
     *
     * @param key      分布式锁
     * @param waitTime 等待时间
     * @param timeUnit 时间单位
     * @param supplier 模板方法
     * @return T 返回值
     * @author Tang
     * @date 2024/1/3 18:31
     **/
    public <T> T executeWithLockThrow(String key, int waitTime, TimeUnit timeUnit, SupplierThrow<T> supplier) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        boolean tryLock = lock.tryLock(waitTime, timeUnit);
        AssertUtil.isTrue(tryLock, CommonErrorEnum.LOCK_LIMIT);
        try {
            return supplier.get();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 上锁并执行模板方法，返回时解锁，无返回值
     *
     * @param key      分布式锁
     * @param waitTime 等待时间
     * @param timeUnit 时间单位
     * @param runnable 模板方法
     * @author Tang
     * @date 2024/1/3 18:36
     **/
    @SneakyThrows
    public void executeWithLock(String key, int waitTime, TimeUnit timeUnit, Runnable runnable) {
        executeWithLockThrow(key, waitTime, timeUnit, () -> {
            runnable.run();
            return null;
        });
    }

    /**
     * 上锁并执行模板方法，返回时解锁，无返回值，无等待时间
     *
     * @param key      分布式锁
     * @param supplier 模板方法
     * @author Tang
     * @date 2024/1/3 18:42
     **/
    @SneakyThrows
    public <T> T executeWithLock(String key, Supplier<T> supplier) {
        return executeWithLockThrow(key, -1, TimeUnit.MILLISECONDS, supplier::get);
    }

    /**
     * 上锁并执行模板方法，返回时解锁，无返回值，无等待时间
     *
     * @param key      分布式锁
     * @param runnable 模板方法
     * @author Tang
     * @date 2024/1/3 18:40
     **/
    public void executeWithLock(String key, Runnable runnable) {
        executeWithLock(key, -1, TimeUnit.MILLISECONDS, runnable);
    }

    @FunctionalInterface
    public interface SupplierThrow<T> {

        /**
         * Gets a result.
         *
         * @return a result
         */
        T get() throws Throwable;
    }
}